package tt.dz.service.imp.middleware;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tt.dz.service.imp.middleware.instruction.Ev3c2Instructions;
import tt.dz.service.imp.middleware.vo.settings.SettingBody;
import tt.dz.tcp.socket.LongConnectionSendMsgImpl;
import tt.dz.tcp.socket.exception.CreateConnectionFailException;
import tt.dz.tcp.socket.exception.DuplicateInstructionException;
import tt.dz.tcp.socket.exception.RetryFailException;
import tt.dz.util.StringUtil;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by luoyp on 2016/6/17.
 * 2.0协议的电桩参数查询服务
 */
@Service
public class  QueryService
{
	private Log log = LogFactory.getLog(QueryService.class);

	@Value("${charger.settings.retrieve.timeout.sec}")
	private int Retrieve_Timeout;//查询超时时间

	private static ConcurrentHashMap<String, String> chargerState = new ConcurrentHashMap<>();//状态查询
	private static ConcurrentHashMap<String, String> userCard = new ConcurrentHashMap<>();//卡号查询
	private static ConcurrentHashMap<String, String> serialNo = new ConcurrentHashMap<>();//电桩标识查询
	private static ConcurrentHashMap<String, String> qrCode = new ConcurrentHashMap<>();//二维码查询
	private static ConcurrentHashMap<String, String> timer = new ConcurrentHashMap<>();//对时查询
	private static ConcurrentHashMap<String, List<String>> chargerWhiteLists = new ConcurrentHashMap<>();//白名单查询
	private static ConcurrentHashMap<String, FeeByPeriod> chargingFee = new ConcurrentHashMap<>();//电费费率查询
	private static ConcurrentHashMap<String, FeeByPeriod> serviceFee = new ConcurrentHashMap<>();//服务费费率查询

	private static HashMap<String, Lock> chargerLockers = new HashMap<>();
	private static HashMap<String, HashMap<String, Condition>> chargerCondition = new HashMap<>();

	@Autowired
	LongConnectionSendMsgImpl longConnection;

	@Autowired
	MessageWrapper messageWrapper;

	public List<String> retrieveWhiteLists(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_WhiteList);
		return chargerWhiteLists.get(chargerId);
	}

	public FeeByPeriod retrieveChargingFee(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_ChargingFee);
		FeeByPeriod f = chargingFee.get(chargerId);
		f.convertFromHexToFloat();
		return f;
	}

	public FeeByPeriod retrieveServiceFee(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_ServiceFee);
		FeeByPeriod f =  serviceFee.get(chargerId);
		f.convertFromHexToFloat();
		return f;
	}

	public String retrieveState(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_State);
		return chargerState.get(chargerId);
	}

	public String retrieveQrcode(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_QrCode);
		return qrCode.get(chargerId);
	}

	public String retrieveTimer(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_Timer);
		return timer.get(chargerId);
	}

	public String retrieveUserCard(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_UserCard);
		return userCard.get(chargerId);
	}

	public String retrieveSerialNo(String chargerId) throws CreateConnectionFailException, DuplicateInstructionException, RetryFailException, InterruptedException, TimeoutException
	{
		retrieveInfo(chargerId, SettingBody.SettingType_SerialNo);
		return serialNo.get(chargerId);
	}

	/**
	 * 查询桩的参数
	 * @param chargerId 桩号
	 * @param settingType 查询类型
	 * @throws CreateConnectionFailException
	 * @throws RetryFailException
	 * @throws DuplicateInstructionException
	 * @throws InterruptedException
	 * @throws TimeoutException
	 */
	private void retrieveInfo(String chargerId, String settingType) throws CreateConnectionFailException, RetryFailException, DuplicateInstructionException, InterruptedException, TimeoutException
	{
		Lock lock = getChargerLock(chargerId); //每个桩有一个独立的lock
		Condition cond = getCondition(chargerId, settingType);

		lock.lock();
		try
		{
			String body = "00" + settingType;
			Ev3c2Instructions instructions = Ev3c2Instructions.SettingsReadWrite;
			String chargerType = "0";
			String portNo = "0";
			String encryption = "00";
			String message = messageWrapper.wrapSendingMsg(body, instructions.code(), chargerId, chargerType, portNo, encryption);
			longConnection.sendAsync(message, chargerId);
			boolean non_timeout = cond.await(Retrieve_Timeout, TimeUnit.SECONDS);
			//如果收到桩查询消息的回复, 该线程会被 tt/dz/service/imp/middleware/ev3c2MessageServiceImpl.java:415 的cond.signalAll()唤醒,
			//唤醒之前各个ConcurrentHashMap中会被写出查询后的值

			//如果未被唤醒, 将抛出超时异常
			if (non_timeout)
				log.info("received response from charger");
			else
				throw new TimeoutException("No respond from charger");
		}
		finally
		{
			lock.unlock();
		}
	}

	public static Lock getChargerLock(String chargerId) //每个桩有一个独立的lock
	{
		Lock lock = chargerLockers.get(chargerId);
		if (lock == null)
		{
			lock = new ReentrantLock();
			chargerLockers.put(chargerId, lock);
		}
		return lock;
	}

	public static Condition getCondition(String chargerId, String settingType)
	{
		HashMap<String, Condition> map = chargerCondition.get(chargerId);
		if (map == null)
		{
			map = new HashMap<>();
			chargerCondition.put(chargerId, map);
		}

		Condition cond = map.get(settingType);

		if (cond == null)
		{
			Lock lock = getChargerLock(chargerId);
			lock.lock();
			try
			{
				if (cond == null)
				{
					cond = getChargerLock(chargerId).newCondition();
					map.put(settingType, cond);
				}
			}
			finally
			{
				lock.unlock();
			}
		}
		return cond;
	}

	public static void putChargerWhiteList(String chargerId, List<String> whiteList)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			chargerWhiteLists.put(chargerId, whiteList);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putChargingFee(String chargerId, List<String> feeList, List<String> periodList)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			FeeByPeriod f = new FeeByPeriod();
			f.feeList = feeList;
			f.periodList = periodList;
			chargingFee.put(chargerId, f);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putServiceFee(String chargerId, List<String> feeList, List<String> periodList)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			FeeByPeriod f = new FeeByPeriod();
			f.feeList = feeList;
			f.periodList = periodList;
			serviceFee.put(chargerId, f);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putChargerState(String chargerId, String state)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			chargerState.put(chargerId, state);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putQrCode(String chargerId, String code)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			qrCode.put(chargerId, code);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putTimer(String chargerId, String t)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			timer.put(chargerId, t);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putUserCard(String chargerId, String card)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			userCard.put(chargerId, card);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static void putSerialNo(String chargerId, String no)
	{
		Lock lock = getChargerLock(chargerId);
		lock.lock();
		try
		{
			serialNo.put(chargerId, no);
		}
		finally
		{
			lock.unlock();
		}
	}

	public static class FeeByPeriod
	{
		public List<String> periodList;
		public List<String> feeList;

		public void convertFromHexToFloat()
		{
			for (int i = 0; i < feeList.size(); i++)
			{
				String s = feeList.get(i);
				feeList.set(i, String.valueOf(StringUtil.float16to10ByParamDesc(s, 100)));
			}
		}

	}
}
